import { Context, Dict, Schema, Session, h } from 'koishi'
import { Kook, KookBot } from "@koishijs/plugin-adapter-kook";
import {} from '@koishijs/plugin-adapter-satori';

import { ImageDetectResult, KookCard, getImageDetectResultCardMsg } from "./kookCards"
import { formatDate } from './utils';
import { run } from 'node:test';

export const name = 'sensitive-image-detect'

export const inject = {
  required: ["http"]
}

interface DetectingRule {
  enable: boolean
  tipInGroup: boolean
  tipInPrivate: boolean
}

export interface Config {
  backendUrl: string
  allReportKookChannelIdList: string[]
  criticalReportKookChannelIdList: string[]
  allReportQQModIdList: string[]
  criticalReportQQModIdList: string[]
  sexDetectingThreshold: number
  politicDetectingThreshold: number
  detectingRules: Dict<DetectingRule, string>
  verboseDebugOutput: boolean
}

export const Config: Schema<Config> = Schema.intersect([
  Schema.object({
    版本号: Schema.string().default('0.0.1a3-20250410').disabled(),
  }).description('版本相关'),

  Schema.object({
    backendUrl: Schema.string().description('后端 RESTful api 服务的url'),
  }).description("Backend-后端服务相关"),

  Schema.object({
    allReportKookChannelIdList: Schema.array(String).description('报告 **所有图片检测记录** Kook频道ID').default([]),
    criticalReportKookChannelIdList: Schema.array(String).description('报告 **疑似涩图记录** Kook频道ID').default([]),
  }).description('kook频道报告'),

  Schema.object({
    allReportQQModIdList: Schema.array(String).description('报告 **所有图片检测记录** 管理员QQ号').default([]),
    criticalReportQQModIdList: Schema.array(String).description('报告 **疑似涩图记录** 管理员QQ号').default([]),
  }).description('qq私聊报告'),

  Schema.object({
    sexDetectingThreshold: Schema.number().min(0).max(1).step(0.05).default(0.9)
      .description("涩图检测阈值"),
    politicDetectingThreshold: Schema.number().min(0).max(1).step(0.05).default(0.95)
      .description("politic图检测阈值"),
    detectingRules: Schema.dict(Schema.object({
      enable: Schema.boolean().description('是否启用').default(true),
      tipInGroup: Schema.boolean().description('是否直接在群聊中提醒').default(true),
      tipInPrivate: Schema.boolean().description('是否私聊提醒给管理员').default(true),
    }).description('群组平台与群组 ID, 格式:`platform:guildId`, 例如:`red:123456`')).description('规则列表')
  }).description('图片检测设置'),

  Schema.object({
    verboseDebugOutput: Schema.boolean().default(false).description('console详细输出')
  })
])


export function apply(ctx: Context, cfg: Config): void {
  // write your plugin here
  ctx.middleware(async (session, next) => {
      if ( session.gid in cfg.detectingRules ){
        ctx.logger.info(`session.gid = ${session.gid}`)
        await solve(session, next);
      }
  });

  // 处理session的逻辑
  // 为什么叫solve？这是写算法题的习惯，对于每一个测试样例 都用一个solve解决
  async function solve(session, next){
    const rule = cfg.detectingRules[session.gid];

    const elements = h.parse(session.content);
    for ( let i=0; i<elements.length; i++ ){
      const element = elements[i];

      if ( element.type === "img"){
        let msg1 = `检测到图片, img消息段 no.${i}\n`;
        // await session.send(msg1);
        if ( cfg.verboseDebugOutput)
          ctx.logger.info(msg1);

        const img_url = element.attrs.file;

        const detectRes = await getSensitiveImageDetectRes(session, img_url, rule.tipInGroup);
        ctx.logger.info(`detectRes = ${JSON.stringify(detectRes)}`);

        if ( !detectRes.isSucceed || !detectRes.res.data ){
          let msg2 = `[后端错误]\ndetectRes.isSucceed===${detectRes.isSucceed}\ndetectRes.res.data===${detectRes.res.data}`;
          if (rule.tipInGroup)
            await session.send(msg2);
          return next();
        }

        const guildInfo = await session.bot.getGuild(session.guildId);

        const imageDetectResult: ImageDetectResult = {
          channelId: session.channelId,
          channelName: guildInfo.name, // 注意：session.channelName 可能为空，取决于平台和事件
          userId: session.userId,
          userName: session.username, // 注意：session.username 可能为空，取决于平台和事件
          timeString: new Date(session.timestamp).toLocaleString(),
          imageUrl: img_url,
          detectResult: detectRes.res.data,
        };
        const allReportMsg = await getImageDetectResultCardMsg(ctx, imageDetectResult);
        // const criticalReportMsg = h.unescape("<kmarkdown>*检测到疑似涩图信息*</kmarkdown>") + '\n' + allReportMsg;
        let criticalReportMsg = "检测到疑似敏感图信息:\n";
        if( imageDetectResult.detectResult.probabilities.sex >= cfg.sexDetectingThreshold )
          criticalReportMsg += "\t疑似是*涩图*\n";
        if ( imageDetectResult.detectResult.probabilities.politic >= cfg.politicDetectingThreshold )
          criticalReportMsg += "\t疑似是*politic相关*\n"
        criticalReportMsg += allReportMsg;


        const kookBot = ctx.bots.find(bot => bot.platform === 'kook');
        for ( const kook_channel_id of cfg.allReportKookChannelIdList ){
          try{
            // ctx.logger.info("11111");
            await kookBot.sendMessage(
              kook_channel_id,
              allReportMsg
            );
          } catch(error){
            ctx.logger.error(`向Kook频道 ${kook_channel_id} 发送allReport失败: ${error}`);
          }
        }

        if ( 
          imageDetectResult.detectResult.probabilities.sex >= cfg.sexDetectingThreshold
          || imageDetectResult.detectResult.probabilities.politic >= cfg.politicDetectingThreshold
        )
        for ( const kook_channel_id of cfg.criticalReportKookChannelIdList ){
          try{
            // ctx.logger.info("22222");
            await kookBot.sendMessage(
              kook_channel_id,
              criticalReportMsg
            )
          } catch (error) {
            ctx.logger.error(`向Kook频道 ${kook_channel_id} 发送criticalReport失败: ${error}`);
          }
        }

        for ( const qq_op_id of cfg.allReportQQModIdList ){
          try{
            // ctx.logger.info("33333");
            // await session.bot.sendPrivateMessage(qq_op_id, "33333");
            await session.bot.sendPrivateMessage(qq_op_id, allReportMsg);
          } catch (err){
            ctx.logger.error(`向QQ号 ${qq_op_id} 发送allReport失败: ${err}`);
          }
        }

        if ( rule.tipInPrivate )
        if ( 
          imageDetectResult.detectResult.probabilities.sex >= cfg.sexDetectingThreshold
          || imageDetectResult.detectResult.probabilities.politic >= cfg.politicDetectingThreshold
        )
        for ( const qq_op_id of cfg.criticalReportQQModIdList ){
          try{
            // ctx.logger.info("44444");
            // await session.bot.sendPrivateMessage(qq_op_id, "44444");
            await session.bot.sendPrivateMessage(qq_op_id, criticalReportMsg);
          } catch (err){
            ctx.logger.error(`向QQ号 ${qq_op_id} 发送criticalReport失败: ${err}`);
          }
        }
      

      }
    }

    return next();
  }

  async function getSensitiveImageDetectRes(session: Session, img_url: string, tipInGroup: boolean){
    try{
      const imageBuffer = await ctx.http.get(img_url, {responseType: 'arraybuffer'});
      const base64Image = Buffer.from(imageBuffer).toString('base64');

      const url = `http://${cfg.backendUrl}/detect_image`;
      const requestData = {
        base64_image: base64Image
      }

      const result = await ctx.http.post(url, requestData);
      // ctx.logger.info(`result.slice(0, 100) = ${JSON.stringify(result).slice(0, 100)}`);
      ctx.logger.info(`in getSensitiveImageDetectRes(), result = ${JSON.stringify(result)}`);

      if ( result.status_code !== 200 ){
        let msg0_error = `检测图片失败，有可能是后端失效，或者其他问题，请联系开发者VincentZyu.`
        if ( tipInGroup )
          await session.send( msg0_error );
        ctx.logger.error(msg0_error);
        return {
          isSucceed: false,
          msg: `error: status_code!==200, code=${result.status_code}`,
          res: result
        };
      }

      const { predicted_class, probabilities } = result.data;
      let msg_2_res = `[SensitiveImageDetectKoishiFrontend]\n检测到图片\n\n` +
      `predicted_class:\n \t${predicted_class}\n`+
      `probabilities:\n ${
        Object.entries(probabilities).map(
          ([k,v]) => `\t${k}: \t${v}`
        ).join('\n')
      }\n`;
      if (tipInGroup)
        await session.send( msg_2_res );
      return {
        isSucceed: true,
        msg: `success`,
        res: result
      }

    } catch(e){
      let msg0_error = `检测图片失败，请联系开发者VincentZyu. error in getSensitiveImageDetectRes(): err:${e}`
      if ( tipInGroup )
        await session.send( msg0_error );
      ctx.logger.error(msg0_error);
      return {
        isSucceed: false,
        msg: `error: ${e}`,
        res: null
      }
    }
  }

}
